#include <atomic>
#include <condition_variable>
#include <iostream>
#include <mutex>

class RdWtLock {
private:
  std::mutex WriteMutex;
  std::mutex ReadMutex;
  std::condition_variable CondWrite;
  std::condition_variable CondRead;
  std::atomic<unsigned int> ReadNum;
  std::atomic<unsigned int> WriteNum;

public:
  RdWtLock():ReadNum(0),WriteNum(0){}
  ~RdWtLock(){}
  void ReadLock();
  void ReadUnlock();
  void WriteLock();
  void WriteUnlock();
};

void RdWtLock::ReadLock() {
  ReadNum.fetch_add(1);
  if (WriteNum.load() != 0) {
    std::unique_lock<std::mutex> lock(ReadMutex);
    CondRead.wait(lock);
  }
}

void RdWtLock::ReadUnlock() {
  ReadNum.fetch_sub(1);
  if (ReadNum.load() == 0) {
    CondWrite.notify_one();
  }
}

void RdWtLock::WriteLock() {
  WriteNum.fetch_add(1);
  std::unique_lock<std::mutex> lock(WriteMutex);
  if(ReadNum.load() != 0){
    CondWrite.wait(lock);
    WriteMutex.lock();
  }
  else if(WriteNum.load() > 1){
    CondWrite.wait(lock);
    WriteMutex.lock();
  }
  std::cout<<"lock"<<std::endl;
}

void RdWtLock::WriteUnlock() {
  WriteNum.fetch_sub(1);
  WriteMutex.unlock();
  if(WriteNum.load() != 0){
    CondWrite.notify_one();
  }
  else{
    CondRead.notify_all();
  }
}

int main() {

  int a = 0;
  RdWtLock lock;
  lock.ReadLock();
  int b = a;
  b++;
  lock.WriteLock();
  a = b;
  lock.WriteUnlock();
  std::cout<<"end"<<std::endl;  
}
